//
//  Matrix4.cpp
//  GSEngine
//
//  Created by Гукун Александр on 1/30/14.
//  Copyright (c) 2014 SNK. All rights reserved.
//

#include "Matrix4.h"

namespace GSEngine
{
    // default costructor
    mat4::mat4()
    {
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
            {
                if (i == j)
                    M[i][j] = 1.0f;
                else
                    M[i][j] = 0.0f;
            }
    }
    // constructor with float array
    mat4::mat4(float* array)
    {
        for (int i = 0; i < 16; i++)
            m[i] = array[i];
    }
    // constructor with floats
    mat4::mat4(float n11, float n12, float n13, float n14,
               float n21, float n22, float n23, float n24,
               float n31, float n32, float n33, float n34,
               float n41, float n42, float n43, float n44)
    {
        m11 = n11; m12 = n12; m13 = n13; m14 = n14;
        m21 = n21; m22 = n22; m23 = n23; m24 = n24;
        m31 = n31; m32 = n32; m33 = n33; m34 = n34;
        m41 = n41; m42 = n42; m43 = n43; m44 = n44;
    }
    
    // minor
    float mat4::minor(int i, int j)
    {
        float elements[9];
        int count = 0;
        for (int indexI = 0; indexI < 4; indexI++)
            for (int indexJ = 0; indexJ < 4; indexJ++)
                if (indexI != i && indexJ != j)
                {
                    elements[count] = M[indexI][indexJ];
                    count++;
                }
        mat3 delta(elements);
        return (delta.determinant());
    }
    // determinant
    float mat4::determinant()
    {
        return (m11*minor(0, 0) - m12*minor(0, 1) + m13*minor(0, 2) - m14*minor(0, 3));
    }
    // set matrix identity
    void mat4::setIdentity()
    {
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
            {
                if (i == j)
                    M[i][j] = 1.0f;
                else
                    M[i][j] = 0.0f;
            }
    }
    // transpose matrix
    void mat4::transpose()
    {
        float temp = m12; m12 = m21; m21 = temp;
        temp = m13; m13 = m31; m31 = temp;
        temp = m23; m23 = m32; m32 = temp;
        temp = m14; m14 = m41; m41 = temp;
        temp = m24; m24 = m42; m42 = temp;
        temp = m43; m43 = m34; m34 = temp;

    }
    // inverse matrix
    void mat4::inverse()
    {
        float delta = determinant();
        if (delta != 0)
        {
            float s = 1 / delta;
            mat4::mat4 complements;
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                {
                    float elemSign = pow(-1, double( i+1 + j+1 ));
                    complements.M[i][j] =  elemSign *  minor(i, j);
                }
            
            complements.transpose();
            for (int i = 0; i < 16; i++)
                m[i] = complements.m[i] * s;
        }
        else
            std::cout << "\nWARNING !!! Determinant = 0. This matrix can't be inversed.\n";
    }
    // print matrix
    void mat4::print()
    {
        std::cout << std::endl;
        std::cout << m11 << " " << m12 << " " << m13 << " " << m14 << std::endl;
        std::cout << m21 << " " << m22 << " " << m23 << " " << m24 << std::endl;
        std::cout << m31 << " " << m32 << " " << m33 << " " << m34 << std::endl;
        std::cout << m41 << " " << m42 << " " << m43 << " " << m44 << std::endl;
        std::cout << std::endl;
    }
    
    // operator = mat4
    void mat4::operator=(mat4 matrix)
    {
        for (int i = 0; i < 16; i++)
            m[i] = matrix.m[i];
    }
    // operator = float array
    void mat4::operator=(float* array)
    {
        for (int i = 0; i < 16; i++)
            m[i] = array[i];
    }
    // operator * float value
    mat4 mat4::operator*(float value)
    {
        mat4 result;
        for (int i = 0; i < 16; i++)
            result.m[i] = m[i] * value;
        
        return result;
    }
    // operator * mat4
    mat4 mat4::operator*(mat4 matrix)
    {
        return mat4(m11 * matrix.m11 + m12 * matrix.m21 + m13 * matrix.m31 + m14 * matrix.m41,
					m11 * matrix.m12 + m12 * matrix.m22 + m13 * matrix.m32 + m14 * matrix.m42,
					m11 * matrix.m13 + m12 * matrix.m23 + m13 * matrix.m33 + m14 * matrix.m43,
					m11 * matrix.m14 + m12 * matrix.m24 + m13 * matrix.m34 + m14 * matrix.m44,
					m21 * matrix.m11 + m22 * matrix.m21 + m23 * matrix.m31 + m24 * matrix.m41,
					m21 * matrix.m12 + m22 * matrix.m22 + m23 * matrix.m32 + m24 * matrix.m42,
					m21 * matrix.m13 + m22 * matrix.m23 + m23 * matrix.m33 + m24 * matrix.m43,
					m21 * matrix.m14 + m22 * matrix.m24 + m23 * matrix.m34 + m24 * matrix.m44,
					m31 * matrix.m11 + m32 * matrix.m21 + m33 * matrix.m31 + m34 * matrix.m41,
					m31 * matrix.m12 + m32 * matrix.m22 + m33 * matrix.m32 + m34 * matrix.m42,
					m31 * matrix.m13 + m32 * matrix.m23 + m33 * matrix.m33 + m34 * matrix.m43,
					m31 * matrix.m14 + m32 * matrix.m24 + m33 * matrix.m34 + m34 * matrix.m44,
					m41 * matrix.m11 + m42 * matrix.m21 + m43 * matrix.m31 + m44 * matrix.m41,
					m41 * matrix.m12 + m42 * matrix.m22 + m43 * matrix.m32 + m44 * matrix.m42,
					m41 * matrix.m13 + m42 * matrix.m23 + m43 * matrix.m33 + m44 * matrix.m43,
					m41 * matrix.m14 + m42 * matrix.m24 + m43 * matrix.m34 + m44 * matrix.m44);
    }
    // returns pointer to float
    const float* mat4::getArrayPtr()
    {
        return &m[0];
    }
}